﻿#ifndef _OI_LT360BASE_DATASTRUCTURE_HPP_
#define _OI_LT360BASE_DATASTRUCTURE_HPP_

#include <QtCore>

#define DATETIMEFORMAT "yyyy-MM-dd_hh-mm-ss-zzz"

namespace LTBase
{
    typedef QList<QPointF> PointCloud;
    typedef QList<QList<QPointF>> Contours;

    enum ToolType
    {
        E_TT_OUTSIDECALIPER = 0,
        E_TT_INSIDECALIPER,
        E_TT_CALIPERPAIR,
        E_TT_HALFCALIPER,
        E_TT_ANGLE,
        E_TT_AREA,
        E_TT_ERRORMAX,
        E_TT_ERRORMEAN,
        E_TT_ERRORRMS,
        E_TT_DEFECTTOOL,
        E_TT_LINEANGLE,
        E_TT_ALIGN_REFERLINE_LEFTMOSTPOINT,
        E_TT_ALIGN_REFERLINE_RIGHTMOSTPOINT,
        E_TT_ALIGN_REFERLINE_REFERPOINT,
        E_TT_ALIGN_THREE_REFERPOINT,
        E_TT_ALIGN_AUTO,
        E_TT_OUTERDIAMETER,
        E_TT_FLATTEN
    };

    enum ToleranceType
    {
        NOTOLERANCE = 0,
        BILATERALTOLERANCE,
        UNILATERALTOLERANCE,
        UPPERLOWER
    };

    inline bool isAlignType(const ToolType& type)
    {
        if (type == E_TT_ALIGN_REFERLINE_LEFTMOSTPOINT) return true;
        if (type == E_TT_ALIGN_REFERLINE_RIGHTMOSTPOINT) return true;
        if (type == E_TT_ALIGN_REFERLINE_REFERPOINT) return true;
        if (type == E_TT_ALIGN_THREE_REFERPOINT) return true;
        if (type == E_TT_ALIGN_AUTO) return true;
        return false;
    }

    static QStringList ToolTypeTranslate = 
    {
        QObject::tr("外卡尺"),
        QObject::tr("内卡尺"),
        QObject::tr("卡尺对"),
        QObject::tr("半卡尺对"),
        QObject::tr("角度"),
        QObject::tr("定位"),
        QObject::tr("最大误差"),
        QObject::tr("平均误差"),
        QObject::tr("均方根误差"),
        QObject::tr("缺陷框"),
        QObject::tr("角度"),
        QObject::tr("最左侧对齐"),
        QObject::tr("最右侧对齐"),
        QObject::tr("参考线和参考点"),
        QObject::tr("三参考点"),
        QObject::tr("自动对准"),
        QObject::tr("外径"),
        QObject::tr("压平")
    };


    static QDataStream &operator<<(QDataStream& stream, const ToolType &type)
    {
        stream << (qint32)type;
        return stream;
    }

    static QDataStream & operator >> (QDataStream & stream, ToolType & type)
    {
        stream >> (quint32&)type;
        return stream;
    }

    static QDataStream& operator<<(QDataStream& stream, const ToleranceType& type)
    {
        stream << (qint32)type;
        return stream;
    }

    static QDataStream& operator >> (QDataStream& stream, ToleranceType& type)
    {
        stream >> (quint32&)type;
        return stream;
    }

    struct MeasurementTool
    {
        MeasurementTool() 
        {}

        MeasurementTool(ToolType mtype, QString mname, QList<QPointF> mdata, double mmaxval, double mminval) : 
            type(mtype),
            name(mname),
            data(mdata),
            maxval(mmaxval),
            minval(mminval)
        { }

        ToolType type;
        QString name;
        QList<QPointF> data;
        double maxval = -1;
        double minval = -1;
        double offset = 0;
        double warningUpper = -1;
        double warningLower = -1;
        double basicSize = 0;
        ToleranceType toleranceType = ToleranceType::NOTOLERANCE;

        QList<QPointF> searchRect(int number)
        {
            switch (number)
            {
            case 1:
                switch (type)
                {
                case E_TT_OUTSIDECALIPER:
                case E_TT_INSIDECALIPER:
                case E_TT_CALIPERPAIR:
                case E_TT_HALFCALIPER:
                case E_TT_ANGLE:
                case E_TT_AREA:
                case E_TT_ERRORMAX:
                case E_TT_ERRORMEAN:
                case E_TT_ERRORRMS:
                    return data.mid(0,4);
                default:
                    break;
                }
                break;
            case 2:
                switch (type)
                {
                case E_TT_CALIPERPAIR:
                    return data.mid(4, 4);
                default:
                    break;
                }
            default:
                break;
            }
            return QList<QPointF>();
        }
    };

    struct DefectTool
    {
        DefectTool()
        {
        }

        DefectTool(QString mname, QList<QPointF> mdata, double merrorXY , double merrorH) :
            name(mname),
            data(mdata),
            errorH(merrorH),
            errorXY(merrorXY)
        {
        }

        QString name = "缺陷工具";
        QList<QPointF> data;
        double errorH = 0.5;
        double errorXY = 1;
        int markingSlot = 1;
    };
    static QDataStream &operator<<(QDataStream& stream, const MeasurementTool &tool)
    {
        stream << tool.type + 3072 << tool.name << tool.data << tool.maxval << tool.minval << tool.offset << tool.warningUpper << tool.warningLower << tool.basicSize << tool.toleranceType;//version 3
        return stream;
    }

    static QDataStream& operator>>(QDataStream& stream, MeasurementTool& tool)
    {
        stream >> tool.type >> tool.name >> tool.data >> tool.maxval >> tool.minval;

        if (tool.type < 1024)//version 0
        {
            tool.offset = 0;
        }
        else if (tool.type < 2048)//version 1
        {
            tool.type = ToolType(tool.type - 1024);
            stream >> tool.offset;
            tool.warningUpper = -1;
            tool.warningLower = -1;
        }
        else if(tool.type < 3072)//version 2
        {
            tool.type = ToolType(tool.type - 2048);
            stream >> tool.offset >> tool.warningUpper >> tool.warningLower;
        }
        else//version 3
        {
            tool.type = ToolType(tool.type - 3072);
            stream >> tool.offset >> tool.warningUpper >> tool.warningLower >> tool.basicSize >> tool.toleranceType;
        }

        return stream;
    }

    //version 2 = AFB0C9D4
    static QDataStream &operator<<(QDataStream& stream, const DefectTool &tool)
    {
        stream << QString("AFB0C9D4") + tool.name << tool.data << tool.errorXY << tool.errorH << tool.markingSlot;
        return stream;
    }

    static QDataStream& operator >> (QDataStream& stream, DefectTool& tool)
    {
        QString name;
        stream >> name;
        if (name.startsWith("AFB0C9D4"))
        {
            tool.name = name.right(name.length() - 8);
            stream >> tool.data >> tool.errorXY >> tool.errorH >> tool.markingSlot;
        }
        else
        {
            tool.name = name;
            stream >> tool.data >> tool.errorXY >> tool.errorH;
        }
        return stream;
    }

    struct SnapShot
    {
        QList<MeasurementTool> toolList;
        QList<DefectTool> defectToolList;
        Contours contour;
        PointCloud design;

        bool isEmpty()
        {
            if (toolList.isEmpty() && defectToolList.isEmpty() && contour.isEmpty() && design.isEmpty())
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    };

    static QDataStream &operator<<(QDataStream& stream, const SnapShot &snapShot)
    {

        stream << snapShot.toolList << snapShot.contour << snapShot.design << snapShot.defectToolList;
        return stream;
    }

    static QDataStream& operator>>(QDataStream& stream, SnapShot& snapShot)
    {
        stream >> snapShot.toolList >> snapShot.contour >> snapShot.design >> snapShot.defectToolList;
        return stream;
    }

    struct AlgoOutput
    {
        MeasurementTool tool;
        double result;
        QList<QPointF> displayData;
    };

    static QDataStream &operator<<(QDataStream& stream, const AlgoOutput &output)
    {
        stream << output.tool << output.result << output.displayData;
        return stream;
    }

    static QDataStream& operator>>(QDataStream& stream, AlgoOutput& output)
    {
        stream >> output.tool >> output.result >> output.displayData;
        return stream;
    }

    struct MeasurementSnapShot
    {
        QList<AlgoOutput> outputList;
        QList<Contours> profiles;
        PointCloud design;
    };

    static QDataStream &operator<<(QDataStream& stream, const MeasurementSnapShot &snapshot)
    {
        stream << snapshot.outputList << snapshot.profiles << snapshot.design;
        return stream;
    }

    static QDataStream& operator>>(QDataStream& stream, MeasurementSnapShot& snapshot)
    {
        stream >> snapshot.outputList >> snapshot.profiles >> snapshot.design;
        return stream;
    }

    struct MeasurementResults
    {
        SnapShot config;
        QMap<QDateTime, QVector<float>> data;
    };

    static QDataStream &operator<<(QDataStream& stream, const MeasurementResults &measurementResults)
    {
        stream << measurementResults.config << measurementResults.data;
        return stream;
    }

    static QDataStream& operator>>(QDataStream& stream, MeasurementResults &measurementResults)
    {
        stream >> measurementResults.config >> measurementResults.data;
        return stream;
    }

    struct CalibData
    {
        enum CameraModel
        {
            HALCON_DIVISION = 0,
            HALCON_POLYNOMIAL,
            OPENCV
        };

        QString cameraName;
        CameraModel cameraModel;
        double homography[9];
        double f = 1;
        double cx = 0;
        double cy = 0;
        double sx = 1;
        double sy = 1;
        double k = 0.0;
        double k1 = 0.0;
        double k2 = 0.0;
        double k3 = 0.0;
        double p1 = 0.0;
        double p2 = 0.0;

        void save(QString fileName)
        {
            QFile out(fileName);
            out.open(QFile::WriteOnly);
            QDataStream outStream(&out);
            outStream << cameraName << (qint32)cameraModel << homography[0] << homography[1] << homography[2] << homography[3] << homography[4] << homography[5] << homography[6] << homography[7] << homography[8] << f << cx << cy << sx << sy << k << k1 << k2 << k3 << p1 << p2;
        }

        CalibData()
        {
            cameraName = "";
            cameraModel = OPENCV;
            homography[0] = 1;
            homography[1] = 0;
            homography[2] = 0;
            homography[3] = 0;
            homography[4] = 1;
            homography[5] = 0;
            homography[6] = 0;
            homography[7] = 0;
            homography[8] = 1;
        }

        CalibData(QString fileName)
        {
            QFile in(fileName);
            in.open(QFile::ReadOnly);
            QDataStream inStream(&in);
            inStream >> cameraName >> (quint32&)cameraModel >> homography[0] >> homography[1] >> homography[2] >> homography[3] >> homography[4] >> homography[5] >> homography[6] >> homography[7] >> homography[8] >> f >> cx >> cy >> sx >> sy >> k >> k1 >> k2 >> k3 >> p1 >> p2;
        }
    };
}

#endif